package com.zzb.service.impl;

import com.zzb.DTO.ResultDTO;
import com.zzb.DTO.UserDTO;
import com.zzb.VO.UserVO;
import com.zzb.dao.UserDao;
import com.zzb.entity.LoginUser;
import com.zzb.entity.UserEntity;
import com.zzb.repostitory.UserRepository;
import com.zzb.service.UserService;
import com.zzb.service.es.ESHighlight;
import com.zzb.service.es.ESUserService;
import com.zzb.util.JwtUtil;
import com.zzb.util.Md5;
import com.zzb.util.Tool;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author ASUS
 */
@Service
public class UserServiceImpl implements UserService {
    // Exchange 的 名字，要和Config里的一致或和rabbitmq里的对应exchange名一样
    private static final String ZZB_IN_HOME_TOPIC_NAME = "zzb.in_home.topic";
    //添加的routingKey
    private static final String ZZB_USER_ADD = "zzb.user.add";

    @Resource
    ElasticsearchRestTemplate elasticsearchTemplate;

    @Resource
    RedisTemplate<String, Object> redisTemplate;

    @Resource
    RabbitTemplate rabbitTemplate;

    @Resource
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    private UserDao userDao;

    @Autowired
    private ESUserService esUserService;

    private UserEntity userEntity = new UserEntity();

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ESHighlight esHighlight;

    @Override
    public ResultDTO login(UserVO userVO, String codeKey) {
//        判断验证码是否正确
        ValueOperations<String, String> stringStringValueOperations = stringRedisTemplate.opsForValue();
        String vCode = stringStringValueOperations.get(codeKey);
        if (userVO.getCode().equals(vCode)) {
            //复制
            BeanUtils.copyProperties(userVO, userEntity);
            //获取用户名、密码认证令牌
            UsernamePasswordAuthenticationToken authenticationToken =
                    new UsernamePasswordAuthenticationToken(userEntity.getUsername(), userEntity.getPassword());
            //进行认证
            Authentication authenticate = authenticationManager.authenticate(authenticationToken);
            //得到认证结果为null
            if (Objects.isNull(authenticate)) {
                throw new RuntimeException("用户名或密码错误");
            }
            //认证通过
            //获取LoginUser
            LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
            //得到用户的ID
            String id = loginUser.getUserEntity().getId().toString();
            //根据用户ID生成token
            String token = JwtUtil.createJWT(id);
            //登录有效时长1小时
            redisTemplate.opsForValue().set("user:" + id, loginUser, 1, TimeUnit.HOURS);
            //返回响应数据
            return ResultDTO.getResult("success", 1, token);
        } else {
            return ResultDTO.getResult("error-code", -1);
        }
    }

    @Override
    public ResultDTO logout() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        Long id = loginUser.getUserEntity().getId();
        redisTemplate.delete("user:" + id);
        return new ResultDTO(200, "退出成功", 0, null);
    }

    @Override
    public ResultDTO changePasswordById(UserVO userVO) {
        BeanUtils.copyProperties(userVO, userEntity);
        //输入的旧密码
        String oldPassword = userEntity.getOldPassword();
        //输入的新密码
        String password = userEntity.getPassword();
        //根据id查找用户
        userEntity = userDao.findById(userEntity);
        //判断输入的密码与原密码是否相等
        if (userEntity.getPassword().equals(Md5.encoderByMd5(oldPassword))) {
            //将新密码设置进去
            userEntity.setPassword(Md5.encoderByMd5(password));
            return ResultDTO.getResult("success", userDao.update(userEntity));
        } else {
            return ResultDTO.getResult("error", 0);
        }
    }


    @Override
    public ResultDTO add(UserVO userVO) {
        //数据复制
        BeanUtils.copyProperties(userVO, userEntity);
        //根据用户名查看redis中这个key是否存在
        //Boolean hasUsername = redisTemplate.hasKey(userEntity.getUsername());
        //创建valueOperations对象，操作redis
        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        //查看redis中用户是否存在
        UserEntity user = (UserEntity) valueOperations.get(userEntity.getUsername());
        if (Objects.isNull(user)) {
            //redis中没有用户，查看数据库中是否有
            if (Objects.isNull(userDao.findByName(userEntity))) {
                //用户不存在，密码加密
                String encodePassword = passwordEncoder.encode(userEntity.getUsername());
                userEntity.setPassword(encodePassword);
                //将信息添加到redis中，设置过期时间为1天
                valueOperations.set(userEntity.getUsername(), userEntity, 1, TimeUnit.DAYS);
                //将消息写入消息队列
                rabbitTemplate.convertAndSend(ZZB_IN_HOME_TOPIC_NAME
                        , ZZB_USER_ADD
                        , "add-" + userEntity.getUsername() + "-添加一个用户");
                //返回添加成功结果
                return ResultDTO.getResult("success", 1);
            }

        }
        //返回添加失败结果
        return ResultDTO.getResult("error", 0);
        /*//判断用户是否存在
        if (userDao.findByName(userEntity) == null) {//不存在
            //加密
            String encodePassword = passwordEncoder.encode(userEntity.getPassword());
            userEntity.setPassword(encodePassword);
            int add = userDao.add(userEntity);
            return ResultDTO.getResult("success", add);
        } else {//用户已存在
            return ResultDTO.getResult("error", 0);
        }*/

    }

    @Override
    public ResultDTO delete(UserVO userVO) {
        //数据复制
        BeanUtils.copyProperties(userVO, userEntity);
        //判断用户是否存在
        if (userDao.findById(userEntity) != null) {
            //存在
            return ResultDTO.getResult("success", userDao.delete(userEntity));
        } else {//用户不存在
            return ResultDTO.getResult("error", 0);
        }
    }

    @Override
    public ResultDTO findById(UserVO userVO) {
        //复制
        BeanUtils.copyProperties(userVO, userEntity);
        //根据id查找
        userEntity = userDao.findById(userEntity);
        if (userEntity != null) {
            //查找到数据
            UserDTO userDTO = new UserDTO();
            BeanUtils.copyProperties(userEntity, userDTO);
            return ResultDTO.getResult("success", userDTO);
        } else {
            //数据没找到
            return ResultDTO.getResult("error", 0);
        }

    }

    @Override
    public ResultDTO findAll(UserVO userVO){
        //复制数据
        BeanUtils.copyProperties(userVO, userEntity);

        //条件判断
        List<Object> determine = esUserService.determine(userEntity);
        BoolQueryBuilder boolQueryBuilder = (BoolQueryBuilder) determine.get(0);
        HighlightBuilder highlightBuilder = (HighlightBuilder) determine.get(1);
        //新建本地查询
        NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
                //查询
                .withQuery(boolQueryBuilder)
                //分页
                .withPageable(PageRequest.of(userEntity.getPage() - 1, userEntity.getLimit()))
                //高亮
                .withHighlightBuilder(highlightBuilder)
                //排序
                .withSort(Sort.by(Sort.Direction.DESC, "id"))
                .build();
        //进行查询，并得到返回结果
        SearchHits<UserEntity> userEntitySearchHits =
                elasticsearchTemplate.search(nativeSearchQuery, UserEntity.class);
        //将SearchHits转化为List
        //List<UserEntity> userEntityList = userEntitySearchHits.stream().map(SearchHit::getContent).collect(Collectors.toList());
        List<UserEntity> userEntityList = userEntitySearchHits.stream()
                .map((x) -> esHighlight.highlight(x)).collect(Collectors.toList());
        //查询的数据条数
        int count = (int) userEntitySearchHits.getTotalHits();


        //查询条数
        /*int count = userDao.count(userEntity);
        //pageHelper分页插件的使用
//        PageHelper.startPage(userEntity.getPage(), userEntity.getLimit());
        //数据库查询数据
        List<UserEntity> list = userDao.findAll(userEntity);*/

        //集合复制
        List<UserDTO> userDTOList = Tool.copy(userEntityList, UserDTO.class);
        return ResultDTO.getResult(count, userDTOList);
    }

    @Override
    public ResultDTO update(UserVO userVO) {
        //数据复制
        BeanUtils.copyProperties(userVO, userEntity);
        //根据id查找
        UserEntity userEntityFindById = userDao.findById(userEntity);
        String newName = userEntity.getUsername();
        String oldName = userEntityFindById.getUsername();
        //判断是不是没有修改名字
        if (oldName.equals(newName)) {
            userEntity.setPassword(userEntityFindById.getPassword());
            int update = userDao.update(userEntity);
            userEntity.setVersion(userEntity.getVersion() + 1);
            userRepository.save(userEntity);
            return ResultDTO.getResult("success", update);
        }
        //根据名称查找用户
        UserEntity userEntityFindByName = userDao.findByName(userEntity);
        if (userEntityFindByName == null) {
            //名称不存在
            userEntity.setPassword(userEntityFindById.getPassword());
            int update = userDao.update(userEntity);
            userEntity.setVersion(userEntity.getVersion() + 1);
            userRepository.save(userEntity);
            return ResultDTO.getResult("success", update);
        }
        //已存在
        return ResultDTO.getResult("error", 0);
    }
}
